import 'dart:convert';

import 'package:dio/dio.dart';

import '../exceptions.dart';
import '../interceptors/log_interceptors.dart';
import '../request.dart';
import '../response.dart';
import 'adapter.dart';

/// dio适配器
/// 基于dio的网络请求工具
class DioAdapter extends JMNetAdapter {
  @override
  Future<JMNetResponse> send(JMNetRequest request) async {
    // 基础配置
    _DioRequestUtil().configBase(request);
    // 请求
    late JMNetResponse response;
    JMNetError? error;
    try {
      response = await _DioRequestUtil().request(request);
    } on JMNetError catch (e) {
      error = e;
    }

    if (error != null) throw error;

    return response;
  }

  /// 实例化上传文件
  static Future<MultipartFile> multipartFileFromFile(String imagePath, String fileName) async {
    return await MultipartFile.fromFile(imagePath, filename: fileName);
  }

  /// 实例化上传文件
  static MultipartFile multipartFileFromBytes(List<int> bytes, String fileName) {
    return MultipartFile.fromBytes(bytes, filename: fileName);
  }

  /// 创建cancelToken
  static CancelToken createCancelToken() {
    return CancelToken();
  }

  /// 关闭请求
  static void cancelRequest(CancelToken cancelToken) {
    cancelToken.cancel('cancelled');
  }
}

/// dio请求工具类
class _DioRequestUtil {
  factory _DioRequestUtil() => _getInstance();

  static _DioRequestUtil? _instance;

  static _DioRequestUtil _getInstance() {
    _instance ??= _DioRequestUtil._internal();
    return _instance!;
  }

  _DioRequestUtil._internal() {
    BaseOptions baseOptions = BaseOptions(
      connectTimeout: const Duration(milliseconds: 10000),
      receiveTimeout: const Duration(milliseconds: 10000),
      // contentType: 'application/json;Charset=UTF-8',
      responseType: ResponseType.json,
    );
    _dio = Dio(baseOptions);
    _dio.interceptors.add(JMNetLogInterceptors());
  }

  late final Dio _dio;

  /// 基础配置
  void configBase(JMNetRequest req) {
    // baseUrl
    if (_dio.options.baseUrl != req.url()) {
      _dio.options = _dio.options.copyWith(baseUrl: req.url());
    }
    // header
    if (_dio.options.headers.toString() != req.headers().toString()) {
      _dio.options = _dio.options.copyWith(headers: req.headers());
    }
    // connectTimeout
    if (_dio.options.connectTimeout?.inSeconds != req.connectTimeout()) {
      _dio.options = _dio.options.copyWith(
        connectTimeout: Duration(seconds: req.connectTimeout()),
      );
    }
    // receiveTimeout
    if (_dio.options.receiveTimeout?.inSeconds != req.receiveTimeout()) {
      _dio.options = _dio.options.copyWith(
        receiveTimeout: Duration(seconds: req.receiveTimeout()),
      );
    }
    // log
    if (req.isLog()) {
      var extra = _dio.options.extra;
      extra.addAll({'log': true});
      _dio.options = _dio.options.copyWith(extra: extra);
    } else {
      var extra = _dio.options.extra;
      extra.addAll({'log': false});
      _dio.options = _dio.options.copyWith(extra: extra);
    }
  }

  /// 统一请求
  Future<JMNetResponse> request(JMNetRequest req) async {
    Response? response;
    DioException? error;
    try {
      if (req.method() == JMNetMethod.get) {
        response = await _dio.get(req.path(), queryParameters: req.params, cancelToken: req.cancelToken());
      }
      if (req.method() == JMNetMethod.post) {
        response = await _dio.post(req.path(), data: FormData.fromMap(req.params), cancelToken: req.cancelToken());
      }
      if (req.method() == JMNetMethod.delete) {
        response = await _dio.delete(req.path(), data: FormData.fromMap(req.params), cancelToken: req.cancelToken());
      }
      if (req.method() == JMNetMethod.put) {
        response = await _dio.put(req.path(), data: FormData.fromMap(req.params), cancelToken: req.cancelToken());
      }
      if (req.method() == JMNetMethod.patch) {
        response = await _dio.patch(req.path(), data: FormData.fromMap(req.params), cancelToken: req.cancelToken());
      }
    } on DioException catch (e) {
      error = e;
      response = e.response;
    }

    // 抛出处理后的异常
    if (error != null) {
      throw JMNetError.create(error);
    }

    // 返回JMNetResponse
    return _handleResponse(response!, req);
  }

  // 构建JMNetResponse
  JMNetResponse _handleResponse(Response response, JMNetRequest req) {
    if (response.statusCode == 200 && response.data != null) {
      var resData = response.data;
      if (response.data is String) {
        try {
          resData = json.decode(response.data);
        } catch (_) {}
      }
      if (response.data is Map<String, dynamic>) {
        try {
          resData = response.data as Map<String, dynamic>;
        } catch (_) {}
      }

      return JMNetResponse(
        data: resData['data'],
        request: req,
        code: resData['code'],
        msg: resData['msg'],
        url: resData['url'],
        extra: response.toString(),
      );
    }

    return JMNetResponse(
      data: null,
      request: req,
      code: response.statusCode,
      msg: response.statusMessage,
      extra: response.toString(),
    );
  }
}
